This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
library(spatstat)
library(sp)
library(rgeos)
library(maptools)
library(GISTools)
install.packages("tidyverse")
library(tmap)
library(sf)
library(geojsonio)
library(tmaptools)
library(tidyverse)
library(rgdal)
library(raster)
library(SciViews)
library(base)
####Basemap
library(tmap)
BoroughMap <- geojson_read("https://raw.githubusercontent.com/ft-interactive/geo-data/master/uk/london/london-wards-2014.geojson", what = "sp")
BNG = "+init=epsg:27700"
BoroughMapBNG <- spTransform(BoroughMap,BNG)
WGS = "+init=epsg:4326"
tmap_mode("view")
tm_shape(BoroughMapBNG) +tm_polygons(col = NA, alpha = 0.5)
#aggregate city of london
library(sf)
library(dplyr)
library(rgdal)
BoroughMapSF <- st_as_sf(BoroughMapBNG)
library(rgeos)
CityofLondon<- gUnionCascaded(BoroughMapBNG[c(630:654), ])
plot(CityofLondon)
CityofLondonSF <- st_as_sf(CityofLondon)
CityofLondonSF$gss_code_ward<-"E05000001" ##add same columns in city of london, for aggregating with other borough
CityofLondonSF$gss_code_borough <- "E09000001"
CityofLondonSF$borough <-"City Of London"
CityofLondonSF$ward <- "City Of London"
BoroughMapSFnew <- BoroughMapSF[-c(630:654), ]
LondonWardSF <- rbind(BoroughMapSFnew,CityofLondonSF) ##Attention New not new
LondonWard <- as(LondonWardSF,"Spatial")
rownames(LondonWardSF) <- 1:nrow(LondonWardSF)
plot(LondonWard)
tm_shape(LondonWard) +tm_polygons(col = NA, alpha = 0.5)
####population density (using https://data.london.gov.uk/dataset/land-area-and-population-density-ward-and-borough 2016)
library(tidyverse)
Density <- read_csv("E:/UCL/005-GI System&Science/GIS-Assessment 3/R assessment3/population_density_2016_final.csv",na = "n/a")
#normalization population_per_square_km
normalization<-function(x){
return((x-min(x))/(max(x)-min(x)))}
density.normalization <- normalization(Density$`population density`)
Density$Density.normalization <- density.normalization
#join the data to basemap P3P27
LondonWard.join.popdens <- LondonWardSF%>% left_join(Density,by=c("gss_code_ward"="New Code"))
qtm(LondonWard.join.popdens,fill="Density.normalization")
#### coexistence of old and new building
#read building list
building <- readOGR("E:/UCL/005-GI System&Science/GIS-Assessment 3/R assessment3/Listed Buildings/ListedBuildings_06Dec2018.shp",layer="ListedBuildings_06Dec2018")
library(maptools)
LondonBoundary<-unionSpatialPolygons(LondonWard,gss_code_borough)
plot(LondonBoundary)
POSAL <- read_csv("E:/UCL/005-GI System&Science/GIS-Assessment 3/R assessment3/access_public_open_space_ward.csv",na = "n/a")
Parsed with column specification:
cols(
WD13CD = [31mcol_character()[39m,
`Ward name` = [31mcol_character()[39m,
`Borough name` = [31mcol_character()[39m,
`Open Space` = [32mcol_double()[39m,
`Local Parks` = [32mcol_double()[39m,
`District Parks` = [32mcol_double()[39m,
`Metropolitan Parks` = [32mcol_double()[39m,
`Regional Parks` = [32mcol_double()[39m
)
POSAL$POSAL.normalization <- normalization(POSAL$`Open Space`)
LondonWard.join.POSAL <- LondonWardSF%>% left_join(POSAL,by=c("gss_code_ward"="WD13CD"))
Column `gss_code_ward`/`WD13CD` joining factor and character vector, coercing into character vector
#qtm(LondonWard.join.POSAL,fill="POSAL.normalization")
#combine two accessibility sub-index
Access.normalization<- merge(POSAL,PTAL, by.x="WD13CD",by.y="Ward Code",all=TRUE)
### Road intersections
node <- readOGR("E:/UCL/005-GI System&Science/GIS-Assessment 3/R assessment3/Road Networkoproad_essh_gb/new_road_node.shp",layer="new_road_node")
BNG = "+init=epsg:27700"
nodeBNG <- spTransform(node, BNG)
summary(node)
##qtm(nodeBNG)
nodeBNGSF <- st_as_sf(nodeBNG)
#select the node type"junction"
nodeBNGSF <- nodeBNGSF[which(nodeBNGSF$formOfNode=="junction"),]
nodeBNGSP <- as(nodeBNGSF, "Spatial")
head(node)
#select node point in borough
nodeBNGSP <- remove.duplicates(nodeBNGSP)
nodeBNGSP.final<- nodeBNGSP[LondonWard,]
# tmap view
tmap_mode("view")
tm_shape(LondonWard) +
tm_polygons(col = NA, alpha = 0.5) +
tm_shape(nodeBNGSP.final) +
tm_dots(col = "blue")
# count number of point in each ward
library(GISTools)
poly.counts(nodeBNGSP.final, LondonWard) -> nodecount
##setNames(nodecount, LondonWard@data$nodecount)
London.node <- LondonWardSF
London.node$nodecount <- nodecount #add number of point to each ward (London.node$nodecount)
#combine the ward area(sq km)
London.node$Square_Kilometres <- LondonWard.join.popdens$`Square Kilometres`
#calculate point/area--point per square kilometre
London.node$PointPerSK <-London.node$nodecount/London.node$Square_Kilometres
###economic vitality
#CLASS 1 house price
#HousePrice <- read_csv("E:/UCL/005-GI System&Science/GIS-Assessment 3/R assessment3/house_price_mean2016.csv",na = "n/a")
#HousePrice.normalization <- normalization(HousePrice$Value)
#HousePrice$HousePrice.normalization <- HousePrice.normalization
#CLASS 2 economic activity
EcoAct <- read_csv("E:/UCL/005-GI System&Science/GIS-Assessment 3/R assessment3/economic_activity_ward.csv",na = "n/a")
EcoAct.normalization <- normalization(EcoAct$`Economically active: Total`)
EcoAct$EcoAct.normalization <- EcoAct.normalization
recreationBNGSF <- union(recreationBNGSF2,recreationBNGSF,makeUniqueIDs = TRUE)
Error in union(recreationBNGSF2, recreationBNGSF, makeUniqueIDs = TRUE) :
unused argument (makeUniqueIDs = TRUE)
# Assign the land use area for each subset. Here we first use the most widespread residential as example
library(raster)
r <- raster(ncols=400, nrows=400) #generate raster size(quantity)
extent(r) <- extent(residentialBNGSP) #Important! Assign the extent of raster to cover the same extents of the polygon
residential.Raster <- rasterize(residentialBNGSP,r,background=NA) #convert polygon to raster
#tmap_mode("view")
#qtm(residential.Raster)+tm_shape(LondonWard) +tm_polygons(col = NA, alpha = 0.5)
#extract rasters from polygons#(extract() cannot use method=bilinear in raster-in-polygon)
residential.extract <- raster::extract(residential.Raster,LondonWard,df=TRUE, weights =FALSE, na.rm = TRUE)
#remove rows with NA
row.has.na <- apply(residential.extract, 1, function(x){any(is.na(x))})
residential.extract2 <-residential.extract[!row.has.na,]
#merge the pixel in same ward(aggregate the frequency of the same data in one)
library(plyr)
residential.extract.sum <- factor(residential.extract2$ID)
residential.extract.sum <- table(residential.extract2$ID)
residential.extract.wardsum <- as.data.frame(residential.extract.sum)
#create new order number for LondonWardSF, for merge the residential.extract and LondonWardSF by order
LondonWard2SF <- LondonWardSF
LondonWard2SF$Var1 <- 1:630
LondonWard2SF[,'Var1']<-factor(LondonWard2SF[,'Var1'])
#merge two dataframe
residential.extract.final <- merge(residential.extract.wardsum, LondonWard2SF, by="Var1",all=TRUE)
#residential.extract.final [!duplicated(residential.extract.final ), ]
#replace NA values with 0
residential.extract.final[is.na(residential.extract.final)] <- 0
#rename the column
colnames(residential.extract.final)[2] <- "Freq.residential"
#raster cell size
#we have to convert raster to polygon because of raster(). This function only compute the pixel area in longitude/latitude coordiante system
residential.repolygon <- rasterToPolygons(residential.Raster, fun=NULL, n=4, na.rm=TRUE, digits=12, dissolve=TRUE)
#qtm(residential.repolygon)
residential.repolygonSF <- st_as_sf(residential.repolygon)
residential.repolygonSF$area <- st_area(residential.repolygonSF)
#so the single pixel area is 28999.54 [m^2](0.029 km^2)
##based on the details above, we create functions for land use analysis
function.raster <- function(landsp){
extent(r) <- extent(landsp)
landsp.Raster <- rasterize(landsp,r,background=NA)
return(landsp.Raster)
}
function.extract<- function(landsp.Raster){
landsp.extract <- raster::extract(landsp.Raster,LondonWard,df=TRUE, weights =FALSE, na.rm = TRUE)
row.has.na <- apply(landsp.extract, 1, function(x){any(is.na(x))})
landsp.extract2 <-landsp.extract[!row.has.na,]
landsp.extract.sum <- factor(landsp.extract2$ID)
landsp.extract.sum <- table(landsp.extract2$ID)
landsp.extract.wardsum <- as.data.frame(landsp.extract.sum)
landsp.extract.final <- merge(landsp.extract.wardsum, LondonWard2SF, by="Var1",all=TRUE)
landsp.extract.final[is.na(landsp.extract.final)] <- 0
return(landsp.extract.final)
}
qtm(residential.Raster)
library(tmap)
tmap_mode("view")
tm_shape(LondonWard) +
tm_polygons(col = NA, alpha = 0.5) +
tm_shape(residentialBNGSP) +
tm_polygons(col = "blue")
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkN0cmwrU2hpZnQrRW50ZXIqLiANCg0KYGBge3J9DQpsaWJyYXJ5KHNwYXRzdGF0KQ0KbGlicmFyeShzcCkNCmxpYnJhcnkocmdlb3MpDQpsaWJyYXJ5KG1hcHRvb2xzKQ0KbGlicmFyeShHSVNUb29scykNCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRtYXApDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShnZW9qc29uaW8pDQpsaWJyYXJ5KHRtYXB0b29scykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZ2RhbCkNCmxpYnJhcnkocmFzdGVyKQ0KbGlicmFyeShTY2lWaWV3cykNCmxpYnJhcnkoYmFzZSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyMjI0Jhc2VtYXANCmxpYnJhcnkodG1hcCkNCkJvcm91Z2hNYXAgPC0gZ2VvanNvbl9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZnQtaW50ZXJhY3RpdmUvZ2VvLWRhdGEvbWFzdGVyL3VrL2xvbmRvbi9sb25kb24td2FyZHMtMjAxNC5nZW9qc29uIiwgd2hhdCA9ICJzcCIpDQpCTkcgPSAiK2luaXQ9ZXBzZzoyNzcwMCINCkJvcm91Z2hNYXBCTkcgPC0gc3BUcmFuc2Zvcm0oQm9yb3VnaE1hcCxCTkcpDQpXR1MgPSAiK2luaXQ9ZXBzZzo0MzI2Ig0KdG1hcF9tb2RlKCJ2aWV3IikNCnRtX3NoYXBlKEJvcm91Z2hNYXBCTkcpICt0bV9wb2x5Z29ucyhjb2wgPSBOQSwgYWxwaGEgPSAwLjUpDQoNCiNhZ2dyZWdhdGUgY2l0eSBvZiBsb25kb24gDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmdkYWwpDQpCb3JvdWdoTWFwU0YgPC0gc3RfYXNfc2YoQm9yb3VnaE1hcEJORykNCmxpYnJhcnkocmdlb3MpDQpDaXR5b2ZMb25kb248LSBnVW5pb25DYXNjYWRlZChCb3JvdWdoTWFwQk5HW2MoNjMwOjY1NCksIF0pDQpwbG90KENpdHlvZkxvbmRvbikNCkNpdHlvZkxvbmRvblNGIDwtIHN0X2FzX3NmKENpdHlvZkxvbmRvbikNCkNpdHlvZkxvbmRvblNGJGdzc19jb2RlX3dhcmQ8LSJFMDUwMDAwMDEiICAjI2FkZCBzYW1lIGNvbHVtbnMgaW4gY2l0eSBvZiBsb25kb24sIGZvciBhZ2dyZWdhdGluZyB3aXRoIG90aGVyIGJvcm91Z2gNCkNpdHlvZkxvbmRvblNGJGdzc19jb2RlX2Jvcm91Z2ggPC0gIkUwOTAwMDAwMSINCkNpdHlvZkxvbmRvblNGJGJvcm91Z2ggPC0iQ2l0eSBPZiBMb25kb24iDQpDaXR5b2ZMb25kb25TRiR3YXJkIDwtICJDaXR5IE9mIExvbmRvbiINCkJvcm91Z2hNYXBTRm5ldyA8LSBCb3JvdWdoTWFwU0ZbLWMoNjMwOjY1NCksIF0NCkxvbmRvbldhcmRTRiA8LSByYmluZChCb3JvdWdoTWFwU0ZuZXcsQ2l0eW9mTG9uZG9uU0YpICMjQXR0ZW50aW9uIE5ldyBub3QgbmV3DQpMb25kb25XYXJkIDwtIGFzKExvbmRvbldhcmRTRiwiU3BhdGlhbCIpDQpyb3duYW1lcyhMb25kb25XYXJkU0YpIDwtIDE6bnJvdyhMb25kb25XYXJkU0YpDQpwbG90KExvbmRvbldhcmQpDQp0bV9zaGFwZShMb25kb25XYXJkKSArdG1fcG9seWdvbnMoY29sID0gTkEsIGFscGhhID0gMC41KQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMjIyNwb3B1bGF0aW9uIGRlbnNpdHkgKHVzaW5nIGh0dHBzOi8vZGF0YS5sb25kb24uZ292LnVrL2RhdGFzZXQvbGFuZC1hcmVhLWFuZC1wb3B1bGF0aW9uLWRlbnNpdHktd2FyZC1hbmQtYm9yb3VnaCAyMDE2KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpEZW5zaXR5IDwtIHJlYWRfY3N2KCJFOi9VQ0wvMDA1LUdJIFN5c3RlbSZTY2llbmNlL0dJUy1Bc3Nlc3NtZW50IDMvUiBhc3Nlc3NtZW50My9wb3B1bGF0aW9uX2RlbnNpdHlfMjAxNl9maW5hbC5jc3YiLG5hID0gIm4vYSIpDQojbm9ybWFsaXphdGlvbiBwb3B1bGF0aW9uX3Blcl9zcXVhcmVfa20NCm5vcm1hbGl6YXRpb248LWZ1bmN0aW9uKHgpew0KcmV0dXJuKCh4LW1pbih4KSkvKG1heCh4KS1taW4oeCkpKX0NCmRlbnNpdHkubm9ybWFsaXphdGlvbiA8LSBub3JtYWxpemF0aW9uKERlbnNpdHkkYHBvcHVsYXRpb24gZGVuc2l0eWApDQpEZW5zaXR5JERlbnNpdHkubm9ybWFsaXphdGlvbiA8LSBkZW5zaXR5Lm5vcm1hbGl6YXRpb24NCg0KI2pvaW4gdGhlIGRhdGEgdG8gYmFzZW1hcCBQM1AyNw0KTG9uZG9uV2FyZC5qb2luLnBvcGRlbnMgPC0gTG9uZG9uV2FyZFNGJT4lIGxlZnRfam9pbihEZW5zaXR5LGJ5PWMoImdzc19jb2RlX3dhcmQiPSJOZXcgQ29kZSIpKQ0KcXRtKExvbmRvbldhcmQuam9pbi5wb3BkZW5zLGZpbGw9IkRlbnNpdHkubm9ybWFsaXphdGlvbiIpDQpgYGANCg0KYGBge3J9DQojIyMjIGNvZXhpc3RlbmNlIG9mIG9sZCBhbmQgbmV3IGJ1aWxkaW5nDQojcmVhZCBidWlsZGluZyBsaXN0DQpidWlsZGluZyA8LSByZWFkT0dSKCJFOi9VQ0wvMDA1LUdJIFN5c3RlbSZTY2llbmNlL0dJUy1Bc3Nlc3NtZW50IDMvUiBhc3Nlc3NtZW50My9MaXN0ZWQgQnVpbGRpbmdzL0xpc3RlZEJ1aWxkaW5nc18wNkRlYzIwMTguc2hwIixsYXllcj0iTGlzdGVkQnVpbGRpbmdzXzA2RGVjMjAxOCIpDQoNCmxpYnJhcnkobWFwdG9vbHMpDQpMb25kb25Cb3VuZGFyeTwtdW5pb25TcGF0aWFsUG9seWdvbnMoTG9uZG9uV2FyZCxnc3NfY29kZV9ib3JvdWdoKQ0KcGxvdChMb25kb25Cb3VuZGFyeSkNCmBgYA0KDQpgYGB7cn0NCiMjIyBQdWJsaWMgdHJhbnNwb3J0IGFjY2Vzc2liaWxpdHkgbGV2ZWwoUFRBTCkNClBUQUwgPC0gcmVhZF9jc3YoIkU6L1VDTC8wMDUtR0kgU3lzdGVtJlNjaWVuY2UvR0lTLUFzc2Vzc21lbnQgMy9SIGFzc2Vzc21lbnQzL1dhcmQyMDE0IEF2cHVibGljIHRyYW5zcG9ydCBhY2Nlc3NpYmlsaXR5IGxldmVsMjAxNS5jc3YiLG5hID0gIm4vYSIpDQojbm9ybWFsaXphdGlvbiBQVEFMDQpub3JtYWxpemF0aW9uPC1mdW5jdGlvbih4KXsNCnJldHVybigoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKSl9DQpQVEFMLm5vcm1hbGl6YXRpb24gPC0gbm9ybWFsaXphdGlvbihQVEFMJEF2UFRBSTIwMTUpDQpQVEFMJFBUQUwubm9ybWFsaXphdGlvbiA8LSBQVEFMLm5vcm1hbGl6YXRpb24NCiNqb2luIHRoZSBkYXRhIHRvIGJhc2VtYXAgUDNQMjcNCkxvbmRvbldhcmQuam9pbi5QVEFMIDwtIExvbmRvbldhcmRTRiU+JSBsZWZ0X2pvaW4oUFRBTCxieT1jKCJnc3NfY29kZV93YXJkIj0iV2FyZCBDb2RlIikpDQpxdG0oTG9uZG9uV2FyZC5qb2luLlBUQUwsZmlsbD0iUFRBTC5ub3JtYWxpemF0aW9uIikNCnF0bShMb25kb25XYXJkLmpvaW4uUFRBTCxmaWxsPSJBdlBUQUkyMDE1IikNCg0KIyMjUHVibGljIG9wZW4gc3BhY2UgYWNjZXNzaWJpbGl0eSBsZXZlbChQT1NBTCkNClBPU0FMIDwtIHJlYWRfY3N2KCJFOi9VQ0wvMDA1LUdJIFN5c3RlbSZTY2llbmNlL0dJUy1Bc3Nlc3NtZW50IDMvUiBhc3Nlc3NtZW50My9hY2Nlc3NfcHVibGljX29wZW5fc3BhY2Vfd2FyZC5jc3YiLG5hID0gIm4vYSIpDQpQT1NBTCRQT1NBTC5ub3JtYWxpemF0aW9uIDwtIG5vcm1hbGl6YXRpb24oUE9TQUwkYE9wZW4gU3BhY2VgKQ0KTG9uZG9uV2FyZC5qb2luLlBPU0FMIDwtIExvbmRvbldhcmRTRiU+JSBsZWZ0X2pvaW4oUE9TQUwsYnk9YygiZ3NzX2NvZGVfd2FyZCI9IldEMTNDRCIpKQ0KI3F0bShMb25kb25XYXJkLmpvaW4uUE9TQUwsZmlsbD0iUE9TQUwubm9ybWFsaXphdGlvbiIpDQojY29tYmluZSB0d28gYWNjZXNzaWJpbGl0eSBzdWItaW5kZXgNCkFjY2Vzcy5ub3JtYWxpemF0aW9uPC0gbWVyZ2UoUE9TQUwsUFRBTCwgYnkueD0iV0QxM0NEIixieS55PSJXYXJkIENvZGUiLGFsbD1UUlVFKQ0KI3JlbW92ZSB1c2VsZXNzIGNvbHVtbiBhbmQgcm93cw0KQWNjZXNzLm5vcm1hbGl6YXRpb25bLGMoNDo4LDEwOjExKV0gPC0gTlVMTA0KI2NhbGN1bGF0ZSB0aGUgbWVhbiBvZiB0d28gYWNjZXNzaWJpbGl0eSBsZXZlbA0KQWNjZXNzLm5vcm1hbGl6YXRpb24kYWNjZXNzLm1lYW4gPC0gcm93TWVhbnMoQWNjZXNzLm5vcm1hbGl6YXRpb25bYygnUFRBTC5ub3JtYWxpemF0aW9uJywgJ1BPU0FMLm5vcm1hbGl6YXRpb24nKV0sIG5hLnJtPVRSVUUpDQpgYGANCg0KYGBge3J9DQojIyMgUm9hZCBpbnRlcnNlY3Rpb25zDQpub2RlIDwtIHJlYWRPR1IoIkU6L1VDTC8wMDUtR0kgU3lzdGVtJlNjaWVuY2UvR0lTLUFzc2Vzc21lbnQgMy9SIGFzc2Vzc21lbnQzL1JvYWQgTmV0d29ya29wcm9hZF9lc3NoX2diL25ld19yb2FkX25vZGUuc2hwIixsYXllcj0ibmV3X3JvYWRfbm9kZSIpDQpCTkcgPSAiK2luaXQ9ZXBzZzoyNzcwMCINCm5vZGVCTkcgPC0gc3BUcmFuc2Zvcm0obm9kZSwgQk5HKQ0Kc3VtbWFyeShub2RlKQ0KIyNxdG0obm9kZUJORykNCm5vZGVCTkdTRiA8LSBzdF9hc19zZihub2RlQk5HKQ0KI3NlbGVjdCB0aGUgbm9kZSB0eXBlImp1bmN0aW9uIg0Kbm9kZUJOR1NGIDwtIG5vZGVCTkdTRlt3aGljaChub2RlQk5HU0YkZm9ybU9mTm9kZT09Imp1bmN0aW9uIiksXQ0Kbm9kZUJOR1NQIDwtIGFzKG5vZGVCTkdTRiwgIlNwYXRpYWwiKQ0KaGVhZChub2RlKQ0KI3NlbGVjdCBub2RlIHBvaW50IGluIGJvcm91Z2gNCm5vZGVCTkdTUCA8LSByZW1vdmUuZHVwbGljYXRlcyhub2RlQk5HU1ApDQpub2RlQk5HU1AuZmluYWw8LSBub2RlQk5HU1BbTG9uZG9uV2FyZCxdDQojIHRtYXAgdmlldw0KdG1hcF9tb2RlKCJ2aWV3IikNCnRtX3NoYXBlKExvbmRvbldhcmQpICsNCiAgdG1fcG9seWdvbnMoY29sID0gTkEsIGFscGhhID0gMC41KSArDQp0bV9zaGFwZShub2RlQk5HU1AuZmluYWwpICsNCiAgdG1fZG90cyhjb2wgPSAiYmx1ZSIpDQoNCiMgY291bnQgbnVtYmVyIG9mIHBvaW50IGluIGVhY2ggd2FyZCANCmxpYnJhcnkoR0lTVG9vbHMpDQpwb2x5LmNvdW50cyhub2RlQk5HU1AuZmluYWwsIExvbmRvbldhcmQpIC0+IG5vZGVjb3VudA0KIyNzZXROYW1lcyhub2RlY291bnQsIExvbmRvbldhcmRAZGF0YSRub2RlY291bnQpDQpMb25kb24ubm9kZSA8LSBMb25kb25XYXJkU0YNCkxvbmRvbi5ub2RlJG5vZGVjb3VudCA8LSBub2RlY291bnQgICAjYWRkIG51bWJlciBvZiBwb2ludCB0byBlYWNoIHdhcmQgKExvbmRvbi5ub2RlJG5vZGVjb3VudCkNCiNjb21iaW5lIHRoZSB3YXJkIGFyZWEoc3Ega20pDQpMb25kb24ubm9kZSRTcXVhcmVfS2lsb21ldHJlcyA8LSBMb25kb25XYXJkLmpvaW4ucG9wZGVucyRgU3F1YXJlIEtpbG9tZXRyZXNgDQojY2FsY3VsYXRlIHBvaW50L2FyZWEtLXBvaW50IHBlciBzcXVhcmUga2lsb21ldHJlDQpMb25kb24ubm9kZSRQb2ludFBlclNLIDwtTG9uZG9uLm5vZGUkbm9kZWNvdW50L0xvbmRvbi5ub2RlJFNxdWFyZV9LaWxvbWV0cmVzDQoNCmBgYA0KDQpgYGB7cn0NCiMjI2Vjb25vbWljIHZpdGFsaXR5DQojQ0xBU1MgMSAgaG91c2UgcHJpY2UNCiNIb3VzZVByaWNlIDwtIHJlYWRfY3N2KCJFOi9VQ0wvMDA1LUdJIFN5c3RlbSZTY2llbmNlL0dJUy1Bc3Nlc3NtZW50IDMvUiBhc3Nlc3NtZW50My9ob3VzZV9wcmljZV9tZWFuMjAxNi5jc3YiLG5hID0gIm4vYSIpDQojSG91c2VQcmljZS5ub3JtYWxpemF0aW9uIDwtIG5vcm1hbGl6YXRpb24oSG91c2VQcmljZSRWYWx1ZSkNCiNIb3VzZVByaWNlJEhvdXNlUHJpY2Uubm9ybWFsaXphdGlvbiA8LSBIb3VzZVByaWNlLm5vcm1hbGl6YXRpb24NCiNDTEFTUyAyIGVjb25vbWljIGFjdGl2aXR5DQpFY29BY3QgPC0gcmVhZF9jc3YoIkU6L1VDTC8wMDUtR0kgU3lzdGVtJlNjaWVuY2UvR0lTLUFzc2Vzc21lbnQgMy9SIGFzc2Vzc21lbnQzL2Vjb25vbWljX2FjdGl2aXR5X3dhcmQuY3N2IixuYSA9ICJuL2EiKQ0KRWNvQWN0Lm5vcm1hbGl6YXRpb24gPC0gbm9ybWFsaXphdGlvbihFY29BY3QkYEVjb25vbWljYWxseSBhY3RpdmU6IFRvdGFsYCkNCkVjb0FjdCRFY29BY3Qubm9ybWFsaXphdGlvbiA8LSBFY29BY3Qubm9ybWFsaXphdGlvbg0KDQpgYGANCg0KYGBge3J9DQojIyMgTGFuZCB1c2UgbWl4DQojaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUGxhbm5pbmdfdXNlX2NsYXNzZXNfaW5fRW5nbGFuZCANCiMgc2l4IGNsYXNzZXMtLQ0KbGFuZGNvdmVyIDwtIHJlYWRPR1IoIkU6L1VDTC8wMDUtR0kgU3lzdGVtJlNjaWVuY2UvR0lTLUFzc2Vzc21lbnQgMy9SIGFzc2Vzc21lbnQzL2dyZWF0ZXItbG9uZG9uLWxhdGVzdC1mcmVlLnNocC9naXNfb3NtX2xhbmR1c2VfYV9mcmVlXzEuc2hwIixsYXllcj0iZ2lzX29zbV9sYW5kdXNlX2FfZnJlZV8xIikNCmxhbmRjb3ZlckJORyA8LSBzcFRyYW5zZm9ybShsYW5kY292ZXIsIEJORykNCmxhbmRjb3ZlckJOR1NGIDwtIHN0X2FzX3NmKGxhbmRjb3ZlckJORykNCg0KQU9JUyA8LSByZWFkT0dSKCJFOi9VQ0wvMDA1LUdJIFN5c3RlbSZTY2llbmNlL0dJUy1Bc3Nlc3NtZW50IDMvUiBhc3Nlc3NtZW50My9ncmVhdGVyLWxvbmRvbi1sYXRlc3QtZnJlZS5zaHAvZ2lzX29zbV9wb2lzX2FfZnJlZV8xLnNocCIsbGF5ZXI9Imdpc19vc21fcG9pc19hX2ZyZWVfMSIpDQpBT0lTQk5HIDwtIHNwVHJhbnNmb3JtKEFPSVMsIEJORykNCkFPSVNCTkdTRiA8LSBzdF9hc19zZihBT0lTQk5HKQ0KDQojZmluZCBtaXNzaW5nIGRhdGEgaW4gZWFjaCBjb2x1bW4gaW4gbGFuZGNvdmVyQk5HU0YNCmNvbFN1bXMoaXMubmEobGFuZGNvdmVyQk5HU0YpKQ0KI2dldCB0aGUgY2xhc3NlcyBvZiBsYW5kY292ZXINCnN1bW1hcnkobGFuZGNvdmVyQk5HU0YpDQojQ0xBU1MxIGdyZWVuc3BhY2UNCmdyZWVuc3BhY2VCTkdTRiA8LSBsYW5kY292ZXJCTkdTRlsgd2hpY2goIGxhbmRjb3ZlckJOR1NGJGZjbGFzcyA9PSJwYXJrIiB8ICBsYW5kY292ZXJCTkdTRiRmY2xhc3MgPT0iZm9yZXN0InxsYW5kY292ZXJCTkdTRiRmY2xhc3MgPT0ibmF0dXJlX3Jlc2VydmUifGxhbmRjb3ZlckJOR1NGJGZjbGFzcyA9PSJncmFzcyIpLF0gDQpncmVlbnNwYWNlQk5HU1AgPC0gYXMoZ3JlZW5zcGFjZUJOR1NGLCJTcGF0aWFsIikNCiNDTEFTUzIgaW5kdXN0cmlhbCBhbmQgY29tbWVyY2lhbA0KaW5kdXN0cmlhbEJOR1NGIDwtIGxhbmRjb3ZlckJOR1NGWyB3aGljaCggbGFuZGNvdmVyQk5HU0YkZmNsYXNzID09ImluZHVzdHJpYWwifCBsYW5kY292ZXJCTkdTRiRmY2xhc3MgPT0iY29tbWVyY2lhbCIpLF0NCmluZHVzdHJpYWxCTkdTUCA8LSBhcyhpbmR1c3RyaWFsQk5HU0YsIlNwYXRpYWwiKQ0KI0NMQVNTIDMgcmVzaWRlbnRpYWwNCnJlc2lkZW50aWFsQk5HU0YgPC0gbGFuZGNvdmVyQk5HU0ZbIHdoaWNoKCBsYW5kY292ZXJCTkdTRiRmY2xhc3MgPT0icmVzaWRlbnRpYWwiICksXQ0KcmVzaWRlbnRpYWxCTkdTUCA8LSBhcyhyZXNpZGVudGlhbEJOR1NGLCJTcGF0aWFsIikNCiNDTEFTUyA0IHJlY3JlYXRpb24tbGVpc3VyZQ0KcmVjcmVhdGlvbkJOR1NGMSA8LSBsYW5kY292ZXJCTkdTRlsgd2hpY2goIGxhbmRjb3ZlckJOR1NGJGZjbGFzcyA9PSJyZWNyZWF0aW9uX2dyb3VuZCJ8IGxhbmRjb3ZlckJOR1NGJGZjbGFzcyA9PSJhbGxvdG1lbnRzIiksXQ0KcmVjcmVhdGlvbkJOR1NGMiA8LSBBT0lTQk5HU0ZbIHdoaWNoKEFPSVNCTkdTRiRmY2xhc3MgPT0iYXR0cmFjdGlvbiJ8QU9JU0JOR1NGJGZjbGFzcyA9PSJ0aGVhdHJlInxBT0lTQk5HU0YkZmNsYXNzID09ImFydHNfY2VudHJlIiksXQ0KcmVjcmVhdGlvbkJOR1NGIDwtIHJiaW5kKHJlY3JlYXRpb25CTkdTRjIscmVjcmVhdGlvbkJOR1NGMSkNCnJlY3JlYXRpb25CTkdTUCA8LSBhcyhyZWNyZWF0aW9uQk5HU0YsIlNwYXRpYWwiKQ0KDQoNCiNDTEFTUyA1IHJldGFpbA0KcmV0YWlsQk5HU0YxIDwtIGxhbmRjb3ZlckJOR1NGWyB3aGljaCggbGFuZGNvdmVyQk5HU0YkZmNsYXNzID09InJldGFpbCIgKSxdDQpyZXRhaWxCTkdTRjIgPC0gQU9JU0JOR1NGWyB3aGljaCggQU9JU0JOR1NGJGZjbGFzcyA9PSJyZXRhaWwiICksXQ0KcmV0aWFsQk5HU0YgPC0gcmJpbmQocmV0YWlsQk5HU0YyLHJldGFpbEJOR1NGMSkNCnJldGFpbEJOR1NQIDwtIGFzKHJldGFpbEJOR1NGLCJTcGF0aWFsIikNCg0KI0NMQVNTIDYtLWNvbW11bml0eSBzZXJ2aWNlcw0KY29tbXVuaXR5Qk5HU0YgPC0gQU9JU0JOR1NGWyB3aGljaChBT0lTQk5HU0YkZmNsYXNzID09InNjaG9vbCJ8QU9JU0JOR1NGJGZjbGFzcyA9PSJob3NwaXRhbCJ8QU9JU0JOR1NGJGZjbGFzcyA9PSJudXJzaW5nX2hvbWUifEFPSVNCTkdTRiRmY2xhc3MgPT0idW5pdmVyc2l0eSIgICksXQ0KY29tbXVuaXR5Qk5HU1AgPC0gYXMoY29tbXVuaXR5Qk5HU0YsIlNwYXRpYWwiKQ0KDQojQ0xBU1MgNy0tdHJhbnNwb3J0DQp0cmFuc3BvcnQgPC0gcmVhZE9HUigiRTovVUNMLzAwNS1HSSBTeXN0ZW0mU2NpZW5jZS9HSVMtQXNzZXNzbWVudCAzL1IgYXNzZXNzbWVudDMvZ3JlYXRlci1sb25kb24tbGF0ZXN0LWZyZWUuc2hwL3JvYWRfcG9seWdvbl9tZXJnZS5zaHAiLGxheWVyPSJyb2FkX3BvbHlnb25fbWVyZ2UiKQ0KdHJhbnNwb3J0Qk5HIDwtIHNwVHJhbnNmb3JtKHRyYW5zcG9ydCwgQk5HKQ0KdHJhbnNwb3J0Qk5HIDwtIHRyYW5zcG9ydEJOR1t0cmFuc3BvcnRCTkdAZGF0YSRBUkVBID4xMDAwMCxdIA0KDQoNCmBgYA0KDQpgYGB7cn0NCiMgQXNzaWduIHRoZSBsYW5kIHVzZSBhcmVhIGZvciBlYWNoIHN1YnNldC4gSGVyZSB3ZSBmaXJzdCB1c2UgdGhlIG1vc3Qgd2lkZXNwcmVhZCByZXNpZGVudGlhbCBhcyBleGFtcGxlIA0KbGlicmFyeShyYXN0ZXIpDQpyIDwtIHJhc3RlcihuY29scz00MDAsIG5yb3dzPTQwMCkgI2dlbmVyYXRlIHJhc3RlciBzaXplKHF1YW50aXR5KQ0KZXh0ZW50KHIpIDwtIGV4dGVudChyZXNpZGVudGlhbEJOR1NQKSAgI0ltcG9ydGFudCEgQXNzaWduIHRoZSBleHRlbnQgb2YgcmFzdGVyIHRvIGNvdmVyIHRoZSBzYW1lIGV4dGVudHMgb2YgdGhlIHBvbHlnb24NCnJlc2lkZW50aWFsLlJhc3RlciA8LSByYXN0ZXJpemUocmVzaWRlbnRpYWxCTkdTUCxyLGJhY2tncm91bmQ9TkEpICNjb252ZXJ0IHBvbHlnb24gdG8gcmFzdGVyDQojdG1hcF9tb2RlKCJ2aWV3IikNCiNxdG0ocmVzaWRlbnRpYWwuUmFzdGVyKSt0bV9zaGFwZShMb25kb25XYXJkKSArdG1fcG9seWdvbnMoY29sID0gTkEsIGFscGhhID0gMC41KQ0KI2V4dHJhY3QgcmFzdGVycyBmcm9tIHBvbHlnb25zIyhleHRyYWN0KCkgY2Fubm90IHVzZSBtZXRob2Q9YmlsaW5lYXIgaW4gcmFzdGVyLWluLXBvbHlnb24pDQpyZXNpZGVudGlhbC5leHRyYWN0IDwtIHJhc3Rlcjo6ZXh0cmFjdChyZXNpZGVudGlhbC5SYXN0ZXIsTG9uZG9uV2FyZCxkZj1UUlVFLCB3ZWlnaHRzID1GQUxTRSwgbmEucm0gPSBUUlVFKQ0KI3JlbW92ZSByb3dzIHdpdGggTkENCnJvdy5oYXMubmEgPC0gYXBwbHkocmVzaWRlbnRpYWwuZXh0cmFjdCwgMSwgZnVuY3Rpb24oeCl7YW55KGlzLm5hKHgpKX0pDQpyZXNpZGVudGlhbC5leHRyYWN0MiA8LXJlc2lkZW50aWFsLmV4dHJhY3RbIXJvdy5oYXMubmEsXQ0KI21lcmdlIHRoZSBwaXhlbCBpbiBzYW1lIHdhcmQoYWdncmVnYXRlIHRoZSBmcmVxdWVuY3kgb2YgdGhlIHNhbWUgZGF0YSBpbiBvbmUpDQpsaWJyYXJ5KHBseXIpDQpyZXNpZGVudGlhbC5leHRyYWN0LnN1bSA8LSBmYWN0b3IocmVzaWRlbnRpYWwuZXh0cmFjdDIkSUQpICANCnJlc2lkZW50aWFsLmV4dHJhY3Quc3VtIDwtIHRhYmxlKHJlc2lkZW50aWFsLmV4dHJhY3QyJElEKQ0KcmVzaWRlbnRpYWwuZXh0cmFjdC53YXJkc3VtIDwtIGFzLmRhdGEuZnJhbWUocmVzaWRlbnRpYWwuZXh0cmFjdC5zdW0pDQojY3JlYXRlIG5ldyBvcmRlciBudW1iZXIgZm9yIExvbmRvbldhcmRTRiwgZm9yIG1lcmdlIHRoZSByZXNpZGVudGlhbC5leHRyYWN0IGFuZCBMb25kb25XYXJkU0YgYnkgb3JkZXINCkxvbmRvbldhcmQyU0YgPC0gTG9uZG9uV2FyZFNGDQpMb25kb25XYXJkMlNGJFZhcjEgPC0gMTo2MzANCkxvbmRvbldhcmQyU0ZbLCdWYXIxJ108LWZhY3RvcihMb25kb25XYXJkMlNGWywnVmFyMSddKQ0KI21lcmdlIHR3byBkYXRhZnJhbWUNCnJlc2lkZW50aWFsLmV4dHJhY3QuZmluYWwgPC0gbWVyZ2UocmVzaWRlbnRpYWwuZXh0cmFjdC53YXJkc3VtLCBMb25kb25XYXJkMlNGLCBieT0iVmFyMSIsYWxsPVRSVUUpDQojcmVzaWRlbnRpYWwuZXh0cmFjdC5maW5hbCBbIWR1cGxpY2F0ZWQocmVzaWRlbnRpYWwuZXh0cmFjdC5maW5hbCApLCBdDQojcmVwbGFjZSBOQSB2YWx1ZXMgd2l0aCAwDQpyZXNpZGVudGlhbC5leHRyYWN0LmZpbmFsW2lzLm5hKHJlc2lkZW50aWFsLmV4dHJhY3QuZmluYWwpXSA8LSAwDQojcmVuYW1lIHRoZSBjb2x1bW4NCmNvbG5hbWVzKHJlc2lkZW50aWFsLmV4dHJhY3QuZmluYWwpWzJdIDwtICJGcmVxLnJlc2lkZW50aWFsIg0KI3Jhc3RlciBjZWxsIHNpemUNCiN3ZSBoYXZlIHRvIGNvbnZlcnQgcmFzdGVyIHRvIHBvbHlnb24gYmVjYXVzZSBvZiByYXN0ZXIoKS4gVGhpcyBmdW5jdGlvbiBvbmx5IGNvbXB1dGUgdGhlIHBpeGVsIGFyZWEgaW4gbG9uZ2l0dWRlL2xhdGl0dWRlIGNvb3JkaWFudGUgc3lzdGVtDQpyZXNpZGVudGlhbC5yZXBvbHlnb24gPC0gcmFzdGVyVG9Qb2x5Z29ucyhyZXNpZGVudGlhbC5SYXN0ZXIsIGZ1bj1OVUxMLCBuPTQsIG5hLnJtPVRSVUUsIGRpZ2l0cz0xMiwgZGlzc29sdmU9VFJVRSkNCiNxdG0ocmVzaWRlbnRpYWwucmVwb2x5Z29uKQ0KcmVzaWRlbnRpYWwucmVwb2x5Z29uU0YgPC0gc3RfYXNfc2YocmVzaWRlbnRpYWwucmVwb2x5Z29uKSANCnJlc2lkZW50aWFsLnJlcG9seWdvblNGJGFyZWEgPC0gc3RfYXJlYShyZXNpZGVudGlhbC5yZXBvbHlnb25TRikNCiNzbyB0aGUgc2luZ2xlIHBpeGVsIGFyZWEgaXMgMjg5OTkuNTQgW21eMl0oMC4wMjkga21eMikNCg0KYGBgDQoNCmBgYHtyfQ0KIyNiYXNlZCBvbiB0aGUgZGV0YWlscyBhYm92ZSwgd2UgY3JlYXRlIGZ1bmN0aW9ucyBmb3IgbGFuZCB1c2UgYW5hbHlzaXMNCmZ1bmN0aW9uLnJhc3RlciA8LSBmdW5jdGlvbihsYW5kc3Apew0KICBleHRlbnQocikgPC0gZXh0ZW50KGxhbmRzcCkNCiAgbGFuZHNwLlJhc3RlciA8LSByYXN0ZXJpemUobGFuZHNwLHIsYmFja2dyb3VuZD1OQSkNCiAgcmV0dXJuKGxhbmRzcC5SYXN0ZXIpDQp9DQoNCmZ1bmN0aW9uLmV4dHJhY3Q8LSBmdW5jdGlvbihsYW5kc3AuUmFzdGVyKXsNCiAgbGFuZHNwLmV4dHJhY3QgPC0gcmFzdGVyOjpleHRyYWN0KGxhbmRzcC5SYXN0ZXIsTG9uZG9uV2FyZCxkZj1UUlVFLCB3ZWlnaHRzID1GQUxTRSwgbmEucm0gPSBUUlVFKQ0KICByb3cuaGFzLm5hIDwtIGFwcGx5KGxhbmRzcC5leHRyYWN0LCAxLCBmdW5jdGlvbih4KXthbnkoaXMubmEoeCkpfSkNCiAgbGFuZHNwLmV4dHJhY3QyIDwtbGFuZHNwLmV4dHJhY3RbIXJvdy5oYXMubmEsXQ0KICBsYW5kc3AuZXh0cmFjdC5zdW0gPC0gZmFjdG9yKGxhbmRzcC5leHRyYWN0MiRJRCkgIA0KICBsYW5kc3AuZXh0cmFjdC5zdW0gPC0gdGFibGUobGFuZHNwLmV4dHJhY3QyJElEKQ0KICBsYW5kc3AuZXh0cmFjdC53YXJkc3VtIDwtIGFzLmRhdGEuZnJhbWUobGFuZHNwLmV4dHJhY3Quc3VtKQ0KICBsYW5kc3AuZXh0cmFjdC5maW5hbCA8LSBtZXJnZShsYW5kc3AuZXh0cmFjdC53YXJkc3VtLCBMb25kb25XYXJkMlNGLCBieT0iVmFyMSIsYWxsPVRSVUUpDQogIGxhbmRzcC5leHRyYWN0LmZpbmFsW2lzLm5hKGxhbmRzcC5leHRyYWN0LmZpbmFsKV0gPC0gMA0KICByZXR1cm4obGFuZHNwLmV4dHJhY3QuZmluYWwpDQp9DQpgYGANCg0KYGBge3J9DQojZ3JlZW4gc3BhY2UgcmFzdGVyDQpncmVlbnNwYWNlLlJhc3RlciA8LSBmdW5jdGlvbi5yYXN0ZXIoZ3JlZW5zcGFjZUJOR1NQKQ0KZ3JlZW5zcGFjZS5leHRyYWN0LmZpbmFsIDwtIGZ1bnRpb24uZXh0cmFjdChncmVlbnNwYWNlLlJhc3RlcikNCmNvbG5hbWVzKGdyZWVuc3BhY2UuZXh0cmFjdC5maW5hbClbMl0gPC0gIkZyZXEuZ3JlZW5zcGFjZSINCg0KI2luZHVzdHJpYWwgYW5kIGNvbW1lcmNpYWwNCmluZHVzdHJpYWwuUmFzdGVyIDwtIGZ1bmN0aW9uLnJhc3RlcihpbmR1c3RyaWFsQk5HU1ApDQppbmR1c3RyaWFsLmV4dHJhY3QuZmluYWwgPC0gZnVuY3Rpb24uZXh0cmFjdChpbmR1c3RyaWFsLlJhc3RlcikNCmNvbG5hbWVzKGluZHVzdHJpYWwuZXh0cmFjdC5maW5hbClbMl0gPC0gIkZyZXEuaW5kdXN0cmlhbCINCiNyZWNyZWF0aW9uDQpyZWNyZWF0aW9uLlJhc3RlciA8LSBmdW5jdGlvbi5yYXN0ZXIocmVjcmVhdGlvbkJOR1NQKQ0KcmVjcmVhdGlvbi5leHRyYWN0LmZpbmFsIDwtIGZ1bmN0aW9uLmV4dHJhY3QocmVjcmVhdGlvbi5SYXN0ZXIpDQpjb2xuYW1lcyhyZWNyZWF0aW9uLmV4dHJhY3QuZmluYWwpWzJdIDwtICJGcmVxLnJlY3JlYXRpb24iDQojcmV0YWlsDQpyZXRhaWwuUmFzdGVyIDwtIGZ1bmN0aW9uLnJhc3RlcihyZWNyZWF0aW9uQk5HU1ApDQpyZXRhaWwuZXh0cmFjdC5maW5hbCA8LSBmdW5jdGlvbi5leHRyYWN0KHJldGFpbC5SYXN0ZXIpDQpjb2xuYW1lcyhyZXRhaWwuZXh0cmFjdC5maW5hbClbMl0gPC0gIkZyZXEucmV0YWlsIg0KI2NvbW11bml0eQ0KY29tbXVuaXR5LlJhc3RlciA8LSBmdW5jdGlvbi5yYXN0ZXIoY29tbXVuaXR5Qk5HU1ApDQpjb21tdW5pdHkuZXh0cmFjdC5maW5hbCA8LSBmdW5jdGlvbi5leHRyYWN0KGNvbW11bml0eS5SYXN0ZXIpDQpjb2xuYW1lcyhjb21tdW5pdHkuZXh0cmFjdC5maW5hbClbMl0gPC0gIkZyZXEuY29tbXVuaXR5Ig0KI3RyYW5zcG9ydA0KdHJhbnNwb3J0LlJhc3RlciA8LSBmdW5jdGlvbi5yYXN0ZXIodHJhbnNwb3J0Qk5HKQ0KdHJhbnNwb3J0LmV4dHJhY3QuZmluYWwgPC0gZnVuY3Rpb24uZXh0cmFjdCh0cmFuc3BvcnQuUmFzdGVyKQ0KY29sbmFtZXModHJhbnNwb3J0LmV4dHJhY3QuZmluYWwpWzJdIDwtICJGcmVxLnRyYW5zcG9ydCINCg0KdG1hcF9tb2RlKCJ2aWV3IikNCnRtX3NoYXBlKExvbmRvbldhcmQpICt0bV9wb2x5Z29ucyhjb2wgPSBOQSwgYWxwaGEgPSAwLjIpK3F0bShyZWNyZWF0aW9uLlJhc3RlcikNCiN0bV9zaGFwZShMb25kb25XYXJkKSArdG1fcG9seWdvbnMoY29sID0gTkEsIGFscGhhID0gMC4yKStxdG0ocmVzaWRlbnRpYWwuUmFzdGVyKQ0KDQojZXh0cmFjdCB1c2VmdWwgY29sdW1ucyBmcm9tIG11bHRpcGxlIGRhdGFmcmFtZQ0KbGFuZHVzZS5kYXRhZnJhbWUgPC0gcmVzaWRlbnRpYWwuZXh0cmFjdC5maW5hbA0KbGFuZHVzZS5kYXRhZnJhbWUgPC0gbWVyZ2UobGFuZHVzZS5kYXRhZnJhbWUsZ3JlZW5zcGFjZS5leHRyYWN0LmZpbmFsLCBieT0iVmFyMSIsYWxsPVRSVUUpDQpsYW5kdXNlLmRhdGFmcmFtZSA8LSBtZXJnZShsYW5kdXNlLmRhdGFmcmFtZSxpbmR1c3RyaWFsLmV4dHJhY3QuZmluYWwsIGJ5PSJWYXIxIixhbGw9VFJVRSkNCmxhbmR1c2UuZGF0YWZyYW1lIDwtIG1lcmdlKGxhbmR1c2UuZGF0YWZyYW1lLHJlY3JlYXRpb24uZXh0cmFjdC5maW5hbCwgYnk9IlZhcjEiLGFsbD1UUlVFKQ0KbGFuZHVzZS5kYXRhZnJhbWUgPC0gbWVyZ2UobGFuZHVzZS5kYXRhZnJhbWUscmV0YWlsLmV4dHJhY3QuZmluYWwsIGJ5PSJWYXIxIixhbGw9VFJVRSkNCmxhbmR1c2UuZGF0YWZyYW1lIDwtIG1lcmdlKGxhbmR1c2UuZGF0YWZyYW1lLGNvbW11bml0eS5leHRyYWN0LmZpbmFsLCBieT0iVmFyMSIsYWxsPVRSVUUpDQpsYW5kdXNlLmRhdGFmcmFtZSA8LSBtZXJnZShsYW5kdXNlLmRhdGFmcmFtZSx0cmFuc3BvcnQuZXh0cmFjdC5maW5hbCwgYnk9IlZhcjEiLGFsbD1UUlVFKQ0KbGFuZHVzZS5kYXRhZnJhbWVbLGMoOToxMywxNToxOSwyMToyNSwyNzozMSwzMzozNywzOTo0MyldIDwtIE5VTEwgI3JlbW92ZSB1c2VsZXNzIGNvbHVtbg0KY29sbmFtZXMobGFuZHVzZS5kYXRhZnJhbWUpWzJdIDwtICJyZXNpZGVudGlhbC5mcmVxIiAjcmVuYW1lIGNvbHVtbg0KaGVhZChsYW5kdXNlLmRhdGFmcmFtZSkNCg0KDQpgYGANCg0KYGBge3J9DQoNCiNjYWxjdWxhdGUgZWFjaCB3YXJkIGFyZWENCmxhbmR1c2UuZGF0YWZyYW1lU0YgPC1sZWZ0X2pvaW4oTG9uZG9uV2FyZFNGLGxhbmR1c2UuZGF0YWZyYW1lLGJ5PWMoImdzc19jb2RlX3dhcmQiPSJnc3NfY29kZV93YXJkLngiKSkNCmxhbmR1c2UuZGF0YWZyYW1lU0Ykd2FyZEFyZWEgPC0gc3RfYXJlYShsYW5kdXNlLmRhdGFmcmFtZVNGKQ0KDQpsaWJyYXJ5KFNjaVZpZXdzKQ0KZnVuY3Rpb24ubGFuZHVzZW1peCA8LSBmdW5jdGlvbihsYW5kdXNlZnJlcSl7DQogIGxhbmR1c2VhcmVhIDwtIGxhbmR1c2VmcmVxKigwLjAyOSowLjAyOSkNCiAgbGFuZHVzZVBlcmNlbnQgPC0gbGFuZHVzZWFyZWEvbGFuZHVzZS5kYXRhZnJhbWVTRiR3YXJkQXJlYQ0KICBMVU0gPC0gbGFuZHVzZVBlcmNlbnQqbG4obGFuZHVzZVBlcmNlbnQpDQogIHJldHVybihMVU0pDQp9DQoNCmxhbmR1c2UuZGF0YWZyYW1lU0YkcmVzaWRlbnRpYWwuTFVNIDwtIGZ1bmN0aW9uLmxhbmR1c2VtaXgobGFuZHVzZS5kYXRhZnJhbWVTRiRyZXNpZGVudGlhbC5mcmVxKQ0KbGFuZHVzZS5kYXRhZnJhbWVTRiRncmVlbnNwYWNlLkxVTSA8LSBmdW5jdGlvbi5sYW5kdXNlbWl4KGxhbmR1c2UuZGF0YWZyYW1lU0YkRnJlcS5ncmVlbnNwYWNlKQ0KbGFuZHVzZS5kYXRhZnJhbWVTRiRpbmR1c3RyaWFsLkxVTSA8LSBmdW5jdGlvbi5sYW5kdXNlbWl4KGxhbmR1c2UuZGF0YWZyYW1lU0YkRnJlcS5pbmR1c3RyaWFsKQ0KbGFuZHVzZS5kYXRhZnJhbWVTRiRyZWNyZWF0aW9uLkxVTSA8LSBmdW5jdGlvbi5sYW5kdXNlbWl4KGxhbmR1c2UuZGF0YWZyYW1lU0YkRnJlcS5yZWNyZWF0aW9uKQ0KbGFuZHVzZS5kYXRhZnJhbWVTRiRyZXRhaWwuTFVNIDwtIGZ1bmN0aW9uLmxhbmR1c2VtaXgobGFuZHVzZS5kYXRhZnJhbWVTRiRGcmVxLnJldGFpbCkNCmxhbmR1c2UuZGF0YWZyYW1lU0YkY29tbXVuaXR5LkxVTSA8LSBmdW5jdGlvbi5sYW5kdXNlbWl4KGxhbmR1c2UuZGF0YWZyYW1lU0YkRnJlcS5jb21tdW5pdHkpDQpsYW5kdXNlLmRhdGFmcmFtZVNGJHRyYW5zcG9ydC5MVU0gPC0gZnVuY3Rpb24ubGFuZHVzZW1peChsYW5kdXNlLmRhdGFmcmFtZVNGJEZyZXEudHJhbnNwb3J0KQ0KbGlicmFyeShiYXNlKQ0KIyMjc3VtIGxhbmR1c2UuTFVNIGFuZCBjYWxjdWxhdGUgaW5kZXgNCmluZGV4LmRhdGFmcmFtZTwtIHN0X3NldF9nZW9tZXRyeShsYW5kdXNlLmRhdGFmcmFtZVNGWyxjKDE6NiwxODoyNSldLE5VTEwpDQojcmVwbGFjZSBOQU4gd2l0aCAwDQppbmRleC5kYXRhZnJhbWVbaXMubmEoaW5kZXguZGF0YWZyYW1lKV0gPC0gMCANCiMjI2NvbXB1dGUgbGFuZCB1c2UgbWl4IGluZGV4DQppbmRleC5kYXRhZnJhbWUkTWl4SW5kZXggPC0gKGFicyhyb3dTdW1zKGluZGV4LmRhdGFmcmFtZVssODoxNF0pKSkvbG4oNykNCg0KcXRtKGluZGV4LmRhdGFmcmFtZVNGLGZpbGw9Ik1peEluZGV4IikNCmBgYA0KDQpgYGB7cn0NCg0KcXRtKHJlc2lkZW50aWFsLlJhc3RlcikNCmxpYnJhcnkodG1hcCkNCnRtYXBfbW9kZSgidmlldyIpDQp0bV9zaGFwZShMb25kb25XYXJkKSArDQogIHRtX3BvbHlnb25zKGNvbCA9IE5BLCBhbHBoYSA9IDAuNSkgKw0KdG1fc2hhcGUocmVzaWRlbnRpYWxCTkdTUCkgKw0KICB0bV9wb2x5Z29ucyhjb2wgPSAiYmx1ZSIpDQpgYGANCg0KYGBge3J9DQojIyNjb21iaW5lIGFsbCBzdWJpbmRleCANCmluZGV4LmRhdGFmcmFtZSA8LSBtZXJnZShpbmRleC5kYXRhZnJhbWUsRGVuc2l0eSxieS54PSJnc3NfY29kZV93YXJkIixieS55PSJOZXcgQ29kZSIsYWxsPVRSVUUpDQojaW5kZXguZGF0YWZyYW1lWywzOjddIDwtIE5VTEwNCmluZGV4LmRhdGFmcmFtZSA8LSBtZXJnZShpbmRleC5kYXRhZnJhbWUsQWNjZXNzLm5vcm1hbGl6YXRpb24sIGJ5Lng9Imdzc19jb2RlX3dhcmQiLGJ5Lnk9IldEMTNDRCIsYWxsPVRSVUUpDQppbmRleC5kYXRhZnJhbWUgPC0gbWVyZ2UoaW5kZXguZGF0YWZyYW1lLEVjb0FjdCwgYnkueD0iZ3NzX2NvZGVfd2FyZCIsYnkueT0iTmV3IENvZGUiLGFsbD1UUlVFKQ0KaW5kZXguZGF0YWZyYW1lIDwtIGluZGV4LmRhdGFmcmFtZVssYygxOjQsMTUsMjAsMjUsMjkpXQ0KIyMjY2FsY3VsYXRlIHRoZSBmaW5hbCBpbmRleCBvZiB3YXJkcyxzZXQgd2VpZ2h0cw0KZnVuY3Rpb24udml0YWxpdHkuaW5kZXggPC0gZnVuY3Rpb24od0xhbmQsd0RlbnNpdHksd0FjY2Vzcyx3RWNvQWN0KXsNCiAgSW5kZXggPC0gKHdMYW5kKSooaW5kZXguZGF0YWZyYW1lWyw1XSkrKHdEZW5zaXR5KSooaW5kZXguZGF0YWZyYW1lWyw2XSkrKHdBY2Nlc3MpKihpbmRleC5kYXRhZnJhbWVbLDddKSsod0Vjb0FjdCkqKGluZGV4LmRhdGFmcmFtZVssOF0pDQogIHJldHVybihJbmRleCkNCn0NCmluZGV4LmRhdGFmcmFtZSRJbmRleCA8LSBmdW5jdGlvbi52aXRhbGl0eS5pbmRleCgwLjUsMC4yLDAuMiwwLjEpDQojam9pbiB3aXRoIExvbmRvbldhcmRTRg0KaW5kZXguZGF0YWZyYW1lU0YgPC1sZWZ0X2pvaW4oTG9uZG9uV2FyZFNGLGluZGV4LmRhdGFmcmFtZSxieT1jKCJnc3NfY29kZV93YXJkIj0iZ3NzX2NvZGVfd2FyZCIpKQ0KcXRtKGluZGV4LmRhdGFmcmFtZVNGLGZpbGw9IkluZGV4IikNCmBgYA0K